package tests.pl.shenlon.poker.cards.evaluation;

import junit.framework.Assert;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import pl.shenlon.poker.cards.Card;
import pl.shenlon.poker.cards.CardRank;
import pl.shenlon.poker.cards.CardSuit;
import pl.shenlon.poker.cards.CardUtils;
import pl.shenlon.poker.cards.Hand;
import pl.shenlon.poker.cards.deck.PokerDeck;
import pl.shenlon.poker.cards.evaluation.HandRank;
import pl.shenlon.poker.cards.evaluation.HandRankEvaluator;

public class HandRankEvaluatorTest {

	private HandRankEvaluator handEvaluator;
	private Hand[] hands;

	@Before
	public void setUp() {
		handEvaluator = HandRankEvaluator.getInstance();
		hands = new Hand[] {
				new Hand(new PokerDeck().getCardsInDeck()),
				new Hand(new PokerDeck().getCardsInDeck()),
		};
		for (int i = 0; i < hands[0].getCardsCount(); i++) {
			hands[0].getCard(i).setFaceUp(true);
		}
	}

	@After
	public void tearDown() {
		hands = null;
	}

	@Test
	public void testEvaluateHand() {
		Card[] handCards = new Card[] {
				new Card(CardSuit.CLUBS, CardRank.KING),
				new Card(CardSuit.DIAMONDS, CardRank.QUEEN),
};
		Card[] tableCards = new Card[]{
				new Card(CardSuit.SPADES, CardRank.JACK),
				new Card(CardSuit.HEARTS, CardRank.TEN),
				new Card(CardSuit.CLUBS, CardRank.NINE),};
		for (Card card : handCards) {
			card.setFaceUp(true);
		}
		for (Card card : tableCards) {
			card.setFaceUp(true);
		}
		Hand hand = new Hand(handCards);
		Hand table = new Hand(tableCards);
		Assert.assertEquals(HandRank.STRAIGHT_KING,
				handEvaluator.evaluateHand(hand, table));
		hand.setCard(0, new Card(CardSuit.CLUBS, CardRank.ACE));
		Assert.assertEquals(HandRank.HIGH_CARD_ACE,
				handEvaluator.evaluateHand(hand, table));
	}

	// @Test
	// public void testGetGroupedCardSuites() {
	// Hand hand = handEvaluator.joinHands(hands);
	// Map<CardSuit, Integer> suits = handEvaluator.getGroupedCardSuits(hand);
	// for (CardSuit suit : suits.keySet()) {
	// Assert.assertEquals(13, (int) suits.get(suit));
	// }
	// for (int i = 0; i < hands[1].getCardsCount(); i++) {
	// hands[1].getCard(i).setFaceUp(true);
	// }
	// hand = handEvaluator.joinHands(hands);
	// suits = handEvaluator.getGroupedCardSuits(hand);
	// for (CardSuit suit : suits.keySet()) {
	// Assert.assertEquals(26, (int) suits.get(suit));
	// }
	// }
	//
	// @Test
	// public void testGetGroupedCardRanks() {
	// Hand hand = handEvaluator.joinHands(hands);
	// Map<CardRank, Integer> ranks = handEvaluator.getGroupedCardRanks(hand);
	// for (CardRank rank : ranks.keySet()) {
	// Assert.assertEquals(4, (int) ranks.get(rank));
	// }
	// for (int i = 0; i < hands[1].getCardsCount(); i++) {
	// hands[1].getCard(i).setFaceUp(true);
	// }
	// hand = handEvaluator.joinHands(hands);
	// ranks = handEvaluator.getGroupedCardRanks(hand);
	// for (CardRank suit : ranks.keySet()) {
	// Assert.assertEquals(8, (int) ranks.get(suit));
	// }
	// }

	@Test
	public void testForFlush() {
		Card[] cards = new Card[]{
				new Card(CardSuit.CLUBS, CardRank.ACE),
				new Card(CardSuit.DIAMONDS, CardRank.ACE),
				new Card(CardSuit.SPADES, CardRank.ACE),
				new Card(CardSuit.SPADES, CardRank.QUEEN),
				new Card(CardSuit.SPADES, CardRank.EIGHT),
				new Card(CardSuit.SPADES, CardRank.EIGHT),
				new Card(CardSuit.SPADES, CardRank.EIGHT),};

		Hand hand = new Hand(cards);
		Assert.assertEquals(HandRank.FLUSH_ACE, handEvaluator.isFlush(hand));
		hand.setCard(2, new Card(CardSuit.SPADES, CardRank.KING));
		Assert.assertEquals(HandRank.FLUSH_KING, handEvaluator.isFlush(hand));
	}

	@Test
	public void testForFourOfAKind() {
		Card[] cards = new Card[]{
				new Card(CardSuit.CLUBS, CardRank.ACE),
				new Card(CardSuit.DIAMONDS, CardRank.ACE),
				new Card(CardSuit.HEARTS, CardRank.ACE),
				new Card(CardSuit.SPADES, CardRank.ACE),
				new Card(CardSuit.SPADES, CardRank.EIGHT),
				new Card(CardSuit.SPADES, CardRank.EIGHT),
				new Card(CardSuit.SPADES, CardRank.EIGHT),
		};

		Hand hand = new Hand(cards);
		Assert.assertEquals(HandRank.FOUR_OF_A_KIND_ACE,
				handEvaluator.isFourOfAKind(hand));
		hand.setCard(3, new Card(CardSuit.SPADES, CardRank.EIGHT));
		Assert.assertEquals(HandRank.FOUR_OF_A_KIND_EIGHT,
				handEvaluator.isFourOfAKind(hand));
		hand.setCard(3, new Card(CardSuit.CLUBS, CardRank.SEVEN));
		Assert.assertEquals(HandRank.UNKNOWN, handEvaluator.isFourOfAKind(hand));
	}

	@Test
	public void testForFullHouse() {
		Card[] cards = new Card[] {
				new Card(CardSuit.CLUBS, CardRank.JACK),
				new Card(CardSuit.HEARTS, CardRank.JACK),
				new Card(CardSuit.SPADES, CardRank.JACK),
				new Card(CardSuit.CLUBS, CardRank.FOUR),
				new Card(CardSuit.DIAMONDS, CardRank.FOUR),
				new Card(CardSuit.CLUBS, CardRank.FIVE),
				new Card(CardSuit.CLUBS, CardRank.TEN),
};
		Hand hand = new Hand(cards);
		Assert.assertEquals(HandRank.FULL_HOUSE_JACK,
				handEvaluator.isFullHouse(hand));
		hand.setCard(2, new Card(CardSuit.HEARTS, CardRank.FOUR));
		Assert.assertEquals(HandRank.FULL_HOUSE_FOUR,
				handEvaluator.isFullHouse(hand));
	}

	@Test
	public void testForPair() {
		Card[] cards = new Card[]{
				new Card(CardSuit.CLUBS, CardRank.ACE),
				new Card(CardSuit.SPADES, CardRank.QUEEN),
				new Card(CardSuit.DIAMONDS, CardRank.EIGHT),
				new Card(CardSuit.SPADES, CardRank.EIGHT),
		};

		Hand hand = new Hand(cards);
		Assert.assertEquals(HandRank.PAIR_EIGHT, handEvaluator.isPair(hand));
	}

	@Test
	public void testForRoyalFlush() {
		Card[] hands = new Card[] {
				new Card(CardSuit.HEARTS, CardRank.ACE),
				new Card(CardSuit.SPADES, CardRank.FIVE),
		};
		Card[] table = new Card[] {
				new Card(CardSuit.HEARTS, CardRank.KING),
				new Card(CardSuit.HEARTS, CardRank.QUEEN),
				new Card(CardSuit.HEARTS, CardRank.JACK),
				new Card(CardSuit.HEARTS, CardRank.NINE),
				new Card(CardSuit.HEARTS, CardRank.TEN),
		};

		for (Card card : hands) {
			card.setFaceUp(true);
		}
		for (Card card : table) {
			card.setFaceUp(true);
		}
		Hand hand = CardUtils.joinHands(new Hand(hands), new Hand(table));
		Assert.assertTrue(handEvaluator.isRoyalFlush(hand).equals(
				HandRank.ROYAL_FLUSH));

		hand.setCard(0, new Card(CardSuit.DIAMONDS, CardRank.ACE));

		Assert.assertFalse(handEvaluator.isRoyalFlush(hand).equals(
				HandRank.ROYAL_FLUSH));
	}

	@Test
	public void testForStraight() {
		Card[] cards = new Card[]{
				// new Card(CardSuit.CLUBS, CardRank.TEN),
				new Card(CardSuit.CLUBS, CardRank.KING),
				new Card(CardSuit.DIAMONDS, CardRank.QUEEN),
				new Card(CardSuit.SPADES, CardRank.JACK),
				new Card(CardSuit.HEARTS, CardRank.TEN),
				new Card(CardSuit.CLUBS, CardRank.NINE),
		// new Card(CardSuit.CLUBS, CardRank.ACE),

		};
		Hand hand = new Hand(cards);
		Assert.assertEquals(HandRank.STRAIGHT_KING,
				handEvaluator.isStraight(hand));

		cards = new Card[]{
				new Card(CardSuit.CLUBS, CardRank.FIVE),
				new Card(CardSuit.CLUBS, CardRank.FOUR),
				new Card(CardSuit.DIAMONDS, CardRank.THREE),
				new Card(CardSuit.SPADES, CardRank.TWO),
				new Card(CardSuit.HEARTS, CardRank.ACE),};
		hand = new Hand(cards);
		Assert.assertEquals(HandRank.STRAIGHT_FIVE,
				handEvaluator.isStraight(hand));
		hand.setCard(0, new Card(CardSuit.HEARTS, CardRank.ACE));
		Assert.assertEquals(HandRank.UNKNOWN, handEvaluator.isStraight(hand));
	}

	@Test
	public void testForStraightFlush() {
		Card[] hands = new Card[]{
				new Card(CardSuit.HEARTS, CardRank.ACE),
				new Card(CardSuit.SPADES, CardRank.FIVE),};
		Card[] table = new Card[]{
				new Card(CardSuit.HEARTS, CardRank.KING),
				new Card(CardSuit.HEARTS, CardRank.QUEEN),
				new Card(CardSuit.HEARTS, CardRank.JACK),
				new Card(CardSuit.HEARTS, CardRank.NINE),
				new Card(CardSuit.HEARTS, CardRank.TEN),};

		for (Card card : hands) {
			card.setFaceUp(true);
		}
		for (Card card : table) {
			card.setFaceUp(true);
		}
		Hand hand = CardUtils.joinHands(new Hand(hands), new Hand(table));

		HandRank rank = handEvaluator.isStraightFlush(hand);
		Assert.assertEquals(HandRank.STRAIGHT_FLUSH_ACE, rank);
	}

	@Test
	public void testForThreeOfAKind() {
		Card[] cards = new Card[]{
				new Card(CardSuit.CLUBS, CardRank.ACE),
				new Card(CardSuit.SPADES, CardRank.QUEEN),
				new Card(CardSuit.CLUBS, CardRank.EIGHT),
				new Card(CardSuit.SPADES, CardRank.EIGHT),};
		Hand hand = new Hand(cards);
		Assert.assertEquals(HandRank.UNKNOWN,
				handEvaluator.isThreeOfAKind(hand));
		hand.setCard(1, new Card(CardSuit.SPADES, CardRank.EIGHT));
		Assert.assertEquals(HandRank.THREE_OF_A_KIND_EIGHT,
				handEvaluator.isThreeOfAKind(hand));

	}
	@Test
	public void testForTwoPair() {
		Card[] cards = new Card[]{
				new Card(CardSuit.SPADES, CardRank.KING),
				new Card(CardSuit.CLUBS, CardRank.ACE),
				new Card(CardSuit.CLUBS, CardRank.ACE),
				new Card(CardSuit.SPADES, CardRank.QUEEN),
				new Card(CardSuit.SPADES, CardRank.JACK),
				new Card(CardSuit.SPADES, CardRank.JACK),
};
		Hand hand = new Hand(cards);
		Assert.assertEquals(HandRank.TWO_PAIR_ACE_JACK_KING,
				handEvaluator.isTwoPair(hand));
		hand.setCard(0, new Card(CardSuit.HEARTS, CardRank.NINE));
		Assert.assertEquals(HandRank.TWO_PAIR_ACE_JACK_NINE,
				handEvaluator.isTwoPair(hand));
	}

	@Test
	public void testJoinHands() {
	
		Hand[] hands = new Hand[] {
				new Hand(new PokerDeck().getCardsInDeck()),
				new Hand(new PokerDeck().getCardsInDeck()),
		};
		Hand hand = CardUtils.joinHands(hands);
		Assert.assertEquals(0, hand.getCards().length);
		hand = CardUtils.joinHands(this.hands);
		Assert.assertEquals(52, hand.getCards().length);

	}
}